import json
import os
import re

from PySide2 import QtWidgets
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QFileDialog, QTreeWidgetItem

from ui.tab_widget.autotest_widget import Ui_AutoTestWidget
from utils.task import QTask


class AutoTest(QtWidgets.QWidget, Ui_AutoTestWidget):

    def __init__(self, parent):
        super(AutoTest, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        # 默认拉伸比例3:1
        self.splitter.setStretchFactor(0, 2)
        self.splitter.setStretchFactor(1, 1)
        self.toolButtonDelete.setVisible(False)
        self.toolButtonDelete.clicked.connect(self.lineEditTestcasePath.clear)
        self.toolButtonDelete.clicked.connect(self.treeWidget.clear)
        self.toolButtonDelete.clicked.connect(self.textBrowser.clear)
        self.lineEditTestcasePath.textChanged.connect(self.set_delete_visible)
        self.pushButtonSelect.clicked.connect(self.select_testcase_path)
        self.pushButtonSelectAll.clicked.connect(self.select_all)
        self.pushButtonSelectNone.clicked.connect(self.select_none)
        self.pushButtonSelectInvert.clicked.connect(self.select_invert)
        self.pushButtonSelectPassed.clicked.connect(self.select_passed)
        self.pushButtonSelectFailed.clicked.connect(self.select_failed)
        self.treeWidget.itemChanged.connect(self.update_tree)

        # 选择的用例以及当前执行的用例索引
        self.selected_cases = []
        self.current_index = 0

        self.task = QTask()

    def set_delete_visible(self, text):
        if not text:
            self.toolButtonDelete.setVisible(False)
        else:
            self.toolButtonDelete.setVisible(True)

    def select_testcase_path(self):
        file_path, _ = QFileDialog.getOpenFileName(self, '选择', os.path.dirname(self.lineEditTestcasePath.text()), 'testcases(*.json)')
        if not file_path:
            return
        self.lineEditTestcasePath.setText(os.path.realpath(file_path))
        self.treeWidget.clear()
        with open(file_path, 'r', encoding='utf-8') as f:
            testcase_list = json.loads(f.read())

        for testcase in testcase_list:
            case_file = testcase.get('case_file')
            description = testcase.get('description')
            self.create_testcase_tree(case_file, description)

    def create_testcase_tree(self, case_file, description):
        self.treeWidget.itemChanged.disconnect(self.update_tree)
        parent = '/'.join(case_file.split('/')[:-1])
        case_no = case_file.split('/')[-1].rstrip('.py')
        for top_level_index in range(self.treeWidget.topLevelItemCount()):
            top_level_item = self.treeWidget.topLevelItem(top_level_index)
            if top_level_item.text(0) == parent:
                current_item = top_level_item
                break
        else:
            current_item = QTreeWidgetItem(self.treeWidget)
            current_item.setText(0, parent)
            current_item.setCheckState(0, Qt.CheckState.Checked)
        child = QTreeWidgetItem(current_item)
        child.setCheckState(0, Qt.CheckState.Checked)
        child.setText(0, case_no)
        child.setData(0, Qt.UserRole, case_file)
        # Qt.UserRole+1 记录用例的状态
        child.setData(0, Qt.UserRole + 1, '')
        child.setText(1, description)
        self.treeWidget.expandAll()
        self.treeWidget.itemChanged.connect(self.update_tree)

    def select_all(self):
        for i in range(self.treeWidget.topLevelItemCount()):
            top_level_item = self.treeWidget.topLevelItem(i)
            top_level_item.setCheckState(0, Qt.CheckState.Checked)

    def select_none(self):
        for i in range(self.treeWidget.topLevelItemCount()):
            top_level_item = self.treeWidget.topLevelItem(i)
            top_level_item.setCheckState(0, Qt.CheckState.Unchecked)

    def select_invert(self):
        for i in range(self.treeWidget.topLevelItemCount()):
            top_level_item = self.treeWidget.topLevelItem(i)
            for j in range(top_level_item.childCount()):
                child = top_level_item.child(j)
                if child.checkState(0) == Qt.CheckState.Checked:
                    child.setCheckState(0, Qt.CheckState.Unchecked)
                else:
                    child.setCheckState(0, Qt.CheckState.Checked)

    def select_passed(self):
        for i in range(self.treeWidget.topLevelItemCount()):
            top_level_item = self.treeWidget.topLevelItem(i)
            for j in range(top_level_item.childCount()):
                child = top_level_item.child(j)
                if child.data(0, Qt.UserRole + 1) == 'passed':
                    child.setCheckState(0, Qt.CheckState.Checked)
                else:
                    child.setCheckState(0, Qt.CheckState.Unchecked)

    def select_failed(self):
        for i in range(self.treeWidget.topLevelItemCount()):
            top_level_item = self.treeWidget.topLevelItem(i)
            for j in range(top_level_item.childCount()):
                child = top_level_item.child(j)
                if child.data(0, Qt.UserRole + 1) == 'failed':
                    child.setCheckState(0, Qt.CheckState.Checked)
                else:
                    child.setCheckState(0, Qt.CheckState.Unchecked)

    def update_tree(self, item: QTreeWidgetItem):
        self.treeWidget.itemChanged.disconnect(self.update_tree)
        parent = item.parent()
        if parent:
            child_count = parent.childCount()
            checked_child = [parent.child(i) for i in range(child_count) if parent.child(i).checkState(0) == Qt.CheckState.Checked]
            if len(checked_child) == 0:
                parent.setCheckState(0, Qt.CheckState.Unchecked)
            elif len(checked_child) == child_count:
                parent.setCheckState(0, Qt.CheckState.Checked)
            else:
                parent.setCheckState(0, Qt.CheckState.PartiallyChecked)
        else:
            child_count = item.childCount()
            for i in range(child_count):
                item.child(i).setCheckState(0, item.checkState(0))

        self.treeWidget.itemChanged.connect(self.update_tree)

    def start_task(self):
        if len(self.parent.devices) != 1:
            QtWidgets.QMessageBox.warning(self, self.windowTitle(), '请确保有且仅有一台RK3568设备连接')
            return
        device = self.parent.devices[0]
        if device.get('mode') == 'Loader':
            QtWidgets.QMessageBox.warning(self, self.windowTitle(), '设备状态异常，请选择Maskrom模式设备')
            return

        if not self.lineEditTestcasePath.text():
            QtWidgets.QMessageBox.warning(self, self.windowTitle(), '请先选择测试用例路径')
            return
        self.selected_cases = self.get_checked_testcases()
        self.current_index = 0
        if not self.selected_cases:
            QtWidgets.QMessageBox.warning(self, self.windowTitle(), '请先选择需要执行的测试用例')
            return

        execute_folder = os.path.dirname(self.lineEditTestcasePath.text())
        case_file_list = [case_item.data(0, Qt.UserRole).strip() for case_item in self.selected_cases]
        self.task.console_sgl.connect(self.show_console)

        self.task.send('run_testcases', device, execute_folder, case_file_list)

    def show_console(self, msg: str):
        if msg == '>>> 开始测试':
            self.parent.statusbar.showMessage('正在执行测试用例，请保持设备处于连接状态...')
            self.toolButtonDelete.setEnabled(False)
            self.pushButtonSelect.setEnabled(False)
            self.textBrowser.clear()
            self.parent.enter_busy()
        elif msg == '>>> 结束测试':
            self.toolButtonDelete.setEnabled(True)
            self.pushButtonSelect.setEnabled(True)
            self.task.console_sgl.disconnect(self.show_console)
            self.parent.exit_busy()
        else:
            self.textBrowser.append(msg)

            line_msg = msg.splitlines()
            for line_data in line_msg:
                if self.current_index >= len(self.selected_cases):
                    break
                current_case_item = self.selected_cases[self.current_index]
                current_case_file = current_case_item.data(0, Qt.UserRole).strip()
                # 当前正在执行的用例
                if line_data.startswith(current_case_file + '::Test::test'):
                    if line_data.upper().endswith('SKIPPED'):
                        self.mark_testcase(current_case_item, '#999999', 'skipped')
                        self.current_index += 1
                        break
                    else:
                        self.mark_testcase(current_case_item, '#FFFF00', '')
                        break
                elif line_data.startswith('PASSED'):
                    self.mark_testcase(current_case_item, '#00AA00', 'passed')
                    self.current_index += 1
                    break
                # failed后面2个空格不能删，以error结尾不能小写转大写，因为hdc命令回显中可能出现这个
                elif line_data.startswith('FAILED  ') or line_data.startswith('ERROR'):
                    self.mark_testcase(current_case_item, '#FF0000', 'failed')
                    self.current_index += 1
                    break

    def get_checked_testcases(self):
        testcases = []
        for top_level_index in range(self.treeWidget.topLevelItemCount()):
            if self.treeWidget.topLevelItem(top_level_index).checkState(0) == Qt.CheckState.Unchecked:
                continue

            top_level_item = self.treeWidget.topLevelItem(top_level_index)
            child_count = top_level_item.childCount()
            for i in range(child_count):
                if top_level_item.child(i).checkState(0) == Qt.CheckState.Checked:
                    testcases.append(top_level_item.child(i))
        return testcases

    def mark_testcase(self, case_item, color, status=''):
        """
        标记用例运行状态
        :param case_item:
        :param color:
        :param status:
        :return:
        """
        case_item.setBackgroundColor(0, color)
        case_item.setBackgroundColor(1, color)
        case_item.setData(0, Qt.UserRole + 1, status)
